package com.desginer.create.single;

/**
 * 单例模式
 * 在单例模式的双重检查锁定（Double-Checked Locking）实现中，使用`volatile`关键字修饰`instance`变量主要有以下原因：
 *
 * 1. **防止指令重排序**：
 *    - 当线程执行`instance = new Singleton()`时，JVM会执行三个步骤：
 *      1. 分配内存空间
 *      2. 初始化对象（调用构造函数）
 *      3. 将引用指向分配的内存地址
 *    - 如果没有`volatile`，JVM可能会进行指令重排序，导致步骤3在步骤2之前执行。这样其他线程可能会看到一个未完全初始化的对象。
 *
 * 2. **保证内存可见性**：
 *    - `volatile`确保当一个线程完成对象的初始化后，其他线程能立即看到最新的值，而不是使用缓存中的旧值。
 *
 * 3. **解决双重检查锁定问题**：
 *    - 在Java 5之前，双重检查锁定存在潜在问题，因为旧的内存模型允许不完全初始化的对象被看到。`volatile`修复了这个问题。
 *
 * 如果不使用`volatile`，可能会出现这样的情况：
 * - 线程A进入同步块开始创建实例
 * - 由于指令重排序，引用被赋值但对象未完全初始化
 * - 线程B检查`instance != null`，返回一个未完全初始化的对象
 *
 * 因此，`volatile`是保证双重检查锁定单例模式正确性的关键。
 *
 * 注意：在Java 5及更高版本中，`volatile`的语义被加强，才使得这种实现方式变得安全可靠。</think>
 */

/**
 * 指令重排序问题：
 * ### **指令重排序（Instruction Reordering）是什么？**
 * 指令重排序是指 **编译器或处理器为了提高程序执行效率，在不改变单线程程序语义的前提下，对指令的执行顺序进行优化调整**。
 *
 * #### **为什么需要指令重排序？**
 * - **提高 CPU 利用率**：现代 CPU 采用流水线（Pipeline）技术，如果某些指令的执行需要等待（如内存加载），CPU 可以先执行后续不依赖的指令，避免空闲。
 * - **优化缓存命中**：调整指令顺序可以减少缓存未命中（Cache Miss），提高性能。
 *
 * ---
 *
 * ### **指令重排序的 3 种来源**
 * 1. **编译器优化**：编译器在生成字节码时可能会调整指令顺序。
 * 2. **CPU 指令级并行（ILP）**：CPU 可能会动态调整指令执行顺序（如乱序执行）。
 * 3. **内存系统重排序**：由于缓存一致性协议（如 MESI），不同 CPU 核心看到的内存操作顺序可能不一致。
 *
 * ---
 *
 * ### **指令重排序的示例场景**
 * #### **场景 1：单例模式中的 `new Singleton()`**
 * ```java
 * instance = new Singleton();  // 非原子操作，可能被重排序
 * ```
 * **正常执行顺序（期望）：**
 * 1. 分配内存空间
 * 2. 调用构造函数（初始化对象）
 * 3. 将引用 `instance` 指向分配的内存
 *
 * **可能的重排序（问题）：**
 * 1. 分配内存空间
 * 2. **将引用 `instance` 指向内存（此时 `instance != null`，但对象未初始化）**
 * 3. 调用构造函数
 *
 * **后果**：
 * - 线程 A 执行 `new Singleton()` 时，由于重排序，`instance` 先被赋值（非 `null`），但对象未初始化。
 * - 线程 B 检查 `if (instance == null)`，发现 `instance` 不为 `null`，直接返回一个**未初始化的对象**，导致程序错误。
 *
 * #### **场景 2：多线程下的变量可见性问题**
 * ```java
 * // 线程 1
 * x = 1;      // 写操作
 * flag = true; // 写操作
 *
 * // 线程 2
 * if (flag) {  // 读操作
 *     System.out.println(x); // 可能输出 0（未看到 x=1 的更新）
 * }
 * ```
 * **可能的执行顺序（由于重排序）：**
 * 1. `flag = true` 先执行（对线程 2 可见）
 * 2. `x = 1` 后执行（线程 2 可能看不到）
 *
 * **后果**：线程 2 可能看到 `flag == true`，但 `x` 仍然是 `0`，导致逻辑错误。
 *
 * ---
 *
 * ### **如何避免指令重排序带来的问题？**
 * 1. **使用 `volatile` 关键字**（如单例模式中的 `instance`）
 *    - 禁止 JVM 和 CPU 对该变量的读写进行重排序。
 *    - 保证变量的修改对所有线程立即可见。
 *
 * 2. **使用 `synchronized` 或 `Lock`**
 *    - 同步块内的代码不会被重排序到块外。
 *
 * 3. **使用 `final` 修饰不可变变量**
 *    - JVM 保证 `final` 变量的初始化不会被重排序。
 *
 * ---
 *
 * ### **总结**
 * - **指令重排序是优化手段**，但在多线程环境下可能导致问题。
 * - **`volatile` 可以禁止重排序**，确保多线程安全。
 * - **单例模式的双重检查锁定必须用 `volatile`**，否则可能返回未初始化的对象。
 */
public class Singleton {

    private static volatile Singleton instance = null;

    private Singleton(){

    }

    public Singleton getInstance(){
        if(instance == null){
            synchronized (Singleton.class) {
                if(instance == null){
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
}
